package com.tyk.bookstore.back.product.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tyk.bookstore.back.common.core.model.type.BinVo;
import com.tyk.bookstore.back.common.core.model.type.SuggestionQuery;
import com.tyk.bookstore.back.common.core.util.AssertUtil;
import com.tyk.bookstore.back.common.core.util.IdGenerator;
import com.tyk.bookstore.back.common.core.util.ObjUtil;
import com.tyk.bookstore.back.product.manager.excel.model.AuthorExcelVo;
import com.tyk.bookstore.back.product.model.entity.AuthorDo;
import com.tyk.bookstore.back.product.model.entity.BookDo;
import com.tyk.bookstore.back.product.model.query.AuthorAddQuery;
import com.tyk.bookstore.back.product.model.query.AuthorPageQuery;
import com.tyk.bookstore.back.product.model.query.AuthorUpdateQuery;
import com.tyk.bookstore.back.product.model.result.AuthorResultEnum;
import com.tyk.bookstore.back.product.model.vo.AuthorPageVo;
import com.tyk.bookstore.back.product.model.vo.AuthorSimVo;
import com.tyk.bookstore.back.product.model.vo.AuthorVo;
import com.tyk.bookstore.back.product.repository.AuthorRepository;
import com.tyk.bookstore.back.product.repository.BookRepository;
import com.tyk.bookstore.back.product.service.AuthorService;
import com.tyk.bookstore.back.product.util.MapStructProduct;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.math.BigInteger;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author tyk
 * @description 作者管理
 */
@Service
@RequiredArgsConstructor
public class AuthorServiceImp implements AuthorService {

    private final AuthorRepository authorRepository;
    private final MapStructProduct mapStructProduct;
    private final BookRepository bookRepository;

    @Override
    public AuthorVo getById(Long id) {
        AuthorDo author = authorRepository.selectById(id);
        AssertUtil.notNull(author, AuthorResultEnum.AUTHOR_NOT_FOUND);
        return mapStructProduct.toAuthorVo(author);
    }

    @Override
    public AuthorPageVo getPage(AuthorPageQuery query) {
        Page<AuthorDo> page = new Page<>(query.getPage(), query.getLimit());
        LambdaQueryWrapper<AuthorDo> w = buildPageQueryWrapper(query);
        authorRepository.selectPage(page, w);
        return mapStructProduct.toAuthorPageVo(page);
    }


    @Override
    public void add(AuthorAddQuery query) {
        // 不存在重复作者
        AssertUtil.isFalse(
                authorRepository.exist2Eq(
                        AuthorDo::getName, query.getName(),
                        AuthorDo::getDescription, query.getDescription(),
                        AuthorDo.class
                ),
                AuthorResultEnum.AUTHOR_ALREADY_EXISTS
        );
        // 添加
        AuthorDo author = mapStructProduct.toAuthorDo(query);
        author.setId(IdGenerator.id());
        authorRepository.insert(author);
    }

    @Override
    public void updateById(AuthorUpdateQuery query) {
        // 作者存在
        AssertUtil.isTrue(
                authorRepository.exist1Eq(AuthorDo::getId, query.getId(), AuthorDo.class),
                AuthorResultEnum.AUTHOR_NOT_FOUND
        );
        // 不存在重复作者
        AssertUtil.isFalse(
                authorRepository.exist2Eq1Ne(
                        AuthorDo::getName, query.getName(),
                        AuthorDo::getDescription, query.getDescription(),
                        AuthorDo::getId, query.getId(),
                        AuthorDo.class
                ),
                AuthorResultEnum.AUTHOR_ALREADY_EXISTS
        );
        // 更新
        AuthorDo author = mapStructProduct.toAuthorDo(query);
        authorRepository.updateById(author);
    }

    @Override
    public void deleteById(Long id) {

        // 作者存在
        AssertUtil.isTrue(
                authorRepository.exist1Eq(
                        AuthorDo::getId, id,
                        AuthorDo.class
                ),
                AuthorResultEnum.AUTHOR_NOT_FOUND
        );

        // 该作者不存在书籍
        AssertUtil.isFalse(
                bookRepository.exist1Eq(
                        BookDo::getAuthorId, id, BookDo.class
                ),
                AuthorResultEnum.AUTHOR_HAS_BOOK
        );

        // 删除
        authorRepository.deleteById(id);
    }

    @Override
    public void deleteBatchById(List<Long> idList) {
        Set<Long> idSet = new HashSet<>(idList);
        Long count = authorRepository.count1In(AuthorDo::getId, idSet, AuthorDo.class);
        AssertUtil.equal(count, Long.valueOf(idList.size()), AuthorResultEnum.AUTHOR_NOT_FOUND);
        authorRepository.deleteBatchIds(idList);
    }

    @Override
    public List<BinVo> getSuggestionList(SuggestionQuery query) {
        return authorRepository
                .<BigInteger, String>selectBinList1Like(
                        AuthorDo::getId, AuthorDo::getName,
                        AuthorDo::getName, query.getKeyword(),
                        AuthorDo.class
                ).stream().map(it -> new BinVo(it.getV1().longValue(), it.getV2())).toList();
    }

    @Override
    public List<AuthorSimVo> getList() {
        return authorRepository
                .<BigInteger, String>selectBinList(AuthorDo::getId, AuthorDo::getName, AuthorDo.class)
                .stream().map(it -> new AuthorSimVo(it.getV1().longValue(), it.getV2())).toList();
    }

    @Override
    public List<AuthorExcelVo> getAuthorExcelVoList(AuthorPageQuery query) {
        LambdaQueryWrapper<AuthorDo> w = buildPageQueryWrapper(query);
        List<AuthorDo> doList = authorRepository.selectList(w);
        return mapStructProduct.toAuthorExcelVoList(doList);
    }

    @Override
    public void saveAuthorExcelVoList(List<AuthorExcelVo> authorList) {
        List<AuthorDo> doList = mapStructProduct.toAuthorDoList(authorList);
        doList.forEach(it -> it.setId(IdGenerator.id()));
        authorRepository.insertBatch(doList);
    }


    /**
     * 构建分页查询Wrapper
     */
    private LambdaQueryWrapper<AuthorDo> buildPageQueryWrapper(AuthorPageQuery query) {

        LambdaQueryWrapper<AuthorDo> w = Wrappers.lambdaQuery();

        w.eq(query.getId() != null, AuthorDo::getId, query.getId());

        w.like(ObjUtil.notBlank(query.getNameLike()), AuthorDo::getName, query.getNameLike());

        w.like(ObjUtil.notBlank(query.getKeyword()), AuthorDo::getDescription, query.getKeyword());

        w.orderBy(true, false, AuthorDo::getCreateTime);

        return w;
    }
}
